interface CodeVerifyOptions {
    id?: string;
    canvasId?: string;
    width?: string;
    height?: string;
    type?: string;
    code?: string;
}

class CodeVerify {
    private options: CodeVerifyOptions;

    constructor(options: CodeVerifyOptions) {
        this.options = {
            id: "",
            canvasId: "verifyCanvas",
            width: "100",
            height: "35",
            type: "blend",
            code: "",
            ...options,
        };

        if (typeof options === "string") {
            this.options.id = options;
        }

        this.options.numArr = "0,1,2,3,4,5,6,7,8,9".split(",");
        this.options.letterArr = this.getAllLetter();

        this.init();
        this.refresh();
    }

    private init(): void {
        const con = document.getElementById(this.options.id);
        const canvas = document.createElement("canvas");
        this.options.width =
            Number(this.options.width) > 0 ? this.options.width : `${con.offsetWidth}`;
        this.options.height =
            Number(this.options.height) > 0 ? this.options.height : `${con.offsetHeight}`;
        canvas.id = this.options.canvasId;
        canvas.width = Number(this.options.width);
        canvas.height = Number(this.options.height);
        canvas.style.cursor = "pointer";
        canvas.innerHTML = "您的浏览器版本不支持canvas";
        con.appendChild(canvas);
        const parent = this;
        canvas.onclick = function () {
            parent.refresh();
        };
    }

    private refresh(): void {
        this.options.code = "";
        const canvas = document.getElementById(this.options.canvasId);
        if (canvas.getContext) {
            const ctx = canvas.getContext("2d");
            if (!ctx) return;
            ctx.textBaseline = "middle";

            ctx.fillStyle = this.randomColor(180, 240);
            ctx.fillRect(0, 0, Number(this.options.width), Number(this.options.height));

            let txtArr: string[] = [];
            if (this.options.type === "blend") {
                txtArr = this.options.numArr.concat(this.options.letterArr);
            } else if (this.options.type === "number") {
                txtArr = this.options.numArr;
            } else {
                txtArr = this.options.letterArr;
            }

            for (let i = 1; i <= 4; i++) {
                const txt = txtArr[this.randomNum(0, txtArr.length)];
                this.options.code += txt;
                ctx.font = `${this.randomNum(
                    Number(this.options.height) / 1.3,
                    Number(this.options.height)
                )}px SimHei`;
                ctx.fillStyle = this.randomColor(50, 160);
                ctx.shadowOffsetX = this.randomNum(-3, 3);
                ctx.shadowOffsetY = this.randomNum(-3, 3);
                ctx.shadowBlur = this.randomNum(-3, 3);
                ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
                const x = (Number(this.options.width) / 5) * i;
                const y = Number(this.options.height) / 2;
                const deg = this.randomNum(-30, 30);
                ctx.translate(x, y);
                ctx.rotate((deg * Math.PI) / 180);
                ctx.fillText(txt, 0, 0);
                ctx.rotate((-deg * Math.PI) / 180);
                ctx.translate(-x, -y);
            }

            for (let i = 0; i < Number(this.options.width) / 4; i++) {
                ctx.fillStyle = this.randomColor(0, 255);
                ctx.beginPath();
                ctx.arc(
                    this.randomNum(0, Number(this.options.width)),
                    this.randomNum(0, Number(this.options.height)),
                    1,
                    0,
                    2 * Math.PI
                );
                ctx.fill();
            }
        }
    }

    validate(code: string): boolean {
        code = code.toLowerCase();
        const v_code = this.options.code.toLowerCase();
        return code === v_code;
    }

    private getAllLetter(): string[] {
        const letterStr = "a,b,c,d,e,f,g,h,i,j,k,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z";
        return letterStr.split(",");
    }

    private randomColor(min: number, max: number): string {
        const r = this.randomNum(min, max);
        const g = this.randomNum(min, max);
        const b = this.randomNum(min, max);
        return `rgb(${r},${g},${b})`;
    }

    private randomNum(min: number, max: number): number {
        return Math.floor(Math.random() * (max - min) + min);
    }
}

export default CodeVerify;